//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================
#include <eltypes.h>
#include "prxstub.h"
#include "marshal.h"
#ifdef _win32
#include <aura.h>
#endif

INLINE ECode ValidateAddress(UInt32 address)
{
    if (address >= PROCESS_LIMIT) {
        MARSHAL_DBGOUT(
            MSHDBG_ERROR, printf("UserToKernMsh: Invalid Address!\n"));
        return E_INVALID_ARGUMENT;
    }
    else {
        return NOERROR;
    }
}

ECode Proxy_UserToKernMsh_BufferSize(
    /* [in] */ const CIMethodEntry *pMethodInfo,
    /* [in] */ UInt32 *puArgs,
    /* [out] */ UInt32 *puInSize,
    /* [out] */ UInt32 *puOutSize)
{
    UInt32 uInSize = 0, uOutSize = 0, i;
    CIBaseType *pParams;
    ECode ec;

    pParams = pMethodInfo->params;
    for (i = 0; i < pMethodInfo->paramNum; i++) {
        switch (BT_TYPE(pParams[i])) {
            case BT_TYPE_UINT8:
            case BT_TYPE_UINT16:
            case BT_TYPE_UINT32:
                puArgs++;
                break;

            case BT_TYPE_PUINT8:
            case BT_TYPE_PUINT16:
            case BT_TYPE_PUINT32:
            case BT_TYPE_PUINT64:
            case BT_TYPE_PEGUID:
            case BT_TYPE_PEMUID:
            case BT_TYPE_PSTRUCT:
            case BT_TYPE_STRUCT: /* Sturct was handled as same as PStruct */
            case BT_TYPE_ASTRING:
            case BT_TYPE_WSTRING:
                ec = ValidateAddress(*puArgs);
                if (FAILED(ec)) return ec;
                puArgs++;
                break;

            case BT_TYPE_UINT64:
#ifdef _mips
                // Adjust for 64bits align on mips
                if (!(pMethodInfo->paramNum % 2)) {
                    puArgs += 1;
                }
#endif
                puArgs += 2;
                break;

            case BT_TYPE_EGUID:
                puArgs += sizeof(EMuid) / 4 + sizeof(wchar_t*) / 4;
                break;

            case BT_TYPE_EMUID:
                puArgs += sizeof(EMuid) / 4;
                break;

            case BT_TYPE_INTERFACE:
                if (BT_IS_IN(pParams[i]) && *puArgs) {
                    uInSize++;
                }
                puArgs++;
                break;

            case BT_TYPE_PINTERFACE:
                if (BT_IS_IN(pParams[i])) {
                    uInSize++;
                }
                else if (BT_IS_OUT(pParams[i])) {
                    uOutSize++;
                }
                puArgs++;
                break;

            case BT_TYPE_WSTRINGBUF:
            case BT_TYPE_ASTRINGBUF:
            case BT_TYPE_BUFFEROF:
            case BT_TYPE_ARRAYOF:
                if (*puArgs
                    && CarQuintetFlag_Type_IObject
                        == (((PCARQUINTET)*puArgs)->m_flags
                                & CarQuintetFlag_TypeMask)
                    && BT_IS_IN(pParams[i])) {
                    int used =
                        ((PCARQUINTET)*puArgs)->m_used / sizeof(IInterface *);
                    int *pBuf = (int*)((PCARQUINTET)*puArgs)->m_pBuf;
                    for (int i = 0; i < used; i++) {
                        if (pBuf[i]) {
                            uInSize++;
                        }
                    }
                }
                puArgs++;
                break;

            default:
                MARSHAL_DBGOUT(MSHDBG_ERROR, printf(
                    "MshUserToKern: Invalid [in, out] type = %08x.\n",
                    pParams[i]));
                assert(0);
                return E_INVALID_ARGUMENT;
        }
    }

    *puInSize = uInSize * sizeof(InterfacePack);
    *puOutSize = uOutSize * sizeof(InterfacePack);
    // uInSize include the User Stack point
    *puInSize += 4;

    return NOERROR;
}

ECode Proxy_UserToKernMsh_In(
    /* [in] */ const CIMethodEntry *pMethodInfo,
    /* [in] */ UInt32 *puArgs,
    /* [in, out] */ UInt32 *puBuffer)
{
    ECode ec = NOERROR;
    int n, cParams;
    CIBaseType *pParams;

    if (GET_IN_INTERFACE_MARK(pMethodInfo->reserved1)) {
        cParams = pMethodInfo->paramNum;
        pParams = pMethodInfo->params;
        for (n = 0; n < cParams; n++) {
            switch (BT_TYPE(pParams[n])) {
                case BT_TYPE_UINT8:
                case BT_TYPE_UINT16:
                case BT_TYPE_UINT32:
                case BT_TYPE_PUINT8:
                case BT_TYPE_PUINT16:
                case BT_TYPE_PUINT32:
                case BT_TYPE_PUINT64:
                case BT_TYPE_PEGUID:
                case BT_TYPE_PEMUID:
                case BT_TYPE_PSTRUCT:
                case BT_TYPE_STRUCT: /* Struct was handled as
                                        same as PStruct */
                case BT_TYPE_ASTRING:
                case BT_TYPE_WSTRING:
                    puArgs++;
                    break;

                case BT_TYPE_UINT64:
#ifdef _mips
                    // Adjust for 64bits align on mips
                    if (!(n % 2)) {
                        puArgs++;
                    }
#endif
                    puArgs += 2;
                    break;

                case BT_TYPE_EGUID:
                    puArgs += sizeof(EMuid) / 4 + sizeof(wchar_t*) / 4;
                    break;

                case BT_TYPE_EMUID:
                    puArgs += sizeof(EMuid) / 4;
                    break;

                case BT_TYPE_INTERFACE:
                    assert(BT_IS_IN(pParams[n]));
                    if (*puArgs) {
                        ec = StdMarshalInterface(
                            (IInterface *)*puArgs,
                            (InterfacePack *)puBuffer);
                        if (FAILED(ec)) {
                            MARSHAL_DBGOUT(MSHDBG_ERROR, printf(
                                "MshProc: marshal interface, ec = %x\n", ec));
                            return ec;
                        }
                        puBuffer = (UInt32 *)((Byte *)puBuffer
                                           + sizeof(InterfacePack));
                    }
                    puArgs++;
                    break;

                case BT_TYPE_PINTERFACE:
                    if (*puArgs
                            && *(UInt32 *)(*puArgs)
                            && BT_IS_IN(pParams[n])) {
                        ec = StdMarshalInterface(
                            (IInterface *)*(UInt32 *)*puArgs,
                            (InterfacePack *)puBuffer);
                        if (FAILED(ec)) {
                            MARSHAL_DBGOUT(MSHDBG_ERROR, printf(
                                "MshProc: marshal interface, ec = %x\n", ec));
                            return ec;
                        }
                    }
                    puBuffer = (UInt32 *)((Byte *)puBuffer
                                        + sizeof(InterfacePack));
                    puArgs++;
                    break;

                case BT_TYPE_WSTRINGBUF:
                case BT_TYPE_ASTRINGBUF:
                case BT_TYPE_BUFFEROF:
                case BT_TYPE_ARRAYOF:
                    if (*puArgs
                        && CarQuintetFlag_Type_IObject
                            == (((PCARQUINTET)*puArgs)->m_flags
                                    & CarQuintetFlag_TypeMask)
                        && BT_IS_IN(pParams[n])) {
                        int used = ((PCARQUINTET)*puArgs)->m_used
                                        / sizeof(IInterface *);
                        int *pBuf = (int*)((PCARQUINTET)*puArgs)->m_pBuf;
                        for (int i = 0; i < used; i++) {
                            if (pBuf[i]) {
                                ec = StdMarshalInterface(
                                    (IInterface *)pBuf[i],
                                    (InterfacePack *)puBuffer);
                                if (FAILED(ec)) {
                                    MARSHAL_DBGOUT(MSHDBG_ERROR, printf(
                                        "MshProc: marshal interface,"
                                        " ec = %x\n", ec));
                                    return ec;
                                }
                                puBuffer = (UInt32 *)((Byte *)puBuffer
                                                   + sizeof(InterfacePack));
                            }
                        }
                    }
                    puArgs++;
                    break;

                default:
                    MARSHAL_DBGOUT(MSHDBG_ERROR, printf(
                        "MshProc: Invalid [in, out] type(%08x).\n",
                        pParams[n]));
                    assert(0);
                    return E_INVALID_ARGUMENT;
            }
        }
    }

    return ec;
}

ECode Proxy_UserToKernUnmsh_Out(
    /* [in] */ const CIMethodEntry *pMethodInfo,
    /* [in] */ UInt32 *puData,
    /* [in, out] */ UInt32 *puArgs)
{
    ECode ec = NOERROR;
    int n, cParams;
    CIBaseType *pParams;

    assert(GET_OUT_INTERFACE_MARK(pMethodInfo->reserved1));
    cParams = pMethodInfo->paramNum;
    pParams = pMethodInfo->params;
    for (n = 0; n < cParams; n++) {
        switch (BT_TYPE(pParams[n])) {
            case BT_TYPE_UINT8:
            case BT_TYPE_UINT16:
            case BT_TYPE_UINT32:
            case BT_TYPE_PUINT8:
            case BT_TYPE_PUINT16:
            case BT_TYPE_PUINT32:
            case BT_TYPE_PUINT64:
            case BT_TYPE_PEGUID:
            case BT_TYPE_PEMUID:
            case BT_TYPE_PSTRUCT:
            case BT_TYPE_STRUCT: /* Struct was handled as same as PStruct */
            case BT_TYPE_ASTRING:
            case BT_TYPE_WSTRING:
            case BT_TYPE_BUFFEROF:
            case BT_TYPE_ARRAYOF:
            case BT_TYPE_WSTRINGBUF:
            case BT_TYPE_ASTRINGBUF:
            case BT_TYPE_INTERFACE:
                puArgs++;
                break;

            case BT_TYPE_UINT64:
#ifdef _mips
                // Adjust for 64bits align on mips
                if (!(n % 2)) {
                    puArgs += 1;
                }
#endif
                puArgs += 2;
                break;

            case BT_TYPE_EGUID:
                puArgs += sizeof(EMuid) / 4 + sizeof(wchar_t*) / 4;
                break;

            case BT_TYPE_EMUID:
                puArgs += sizeof(EMuid) / 4;
                break;

            case BT_TYPE_PINTERFACE:
                if (*puArgs && BT_IS_OUT(pParams[n])) {
                    if (((InterfacePack *)puData)->m_oid != 0) {
                        ec = StdUnmarshalInterface(
                            (InterfacePack *)puData,
                            CTX_USER_2_KERN,
                            FALSE,
                            (IInterface **)(*puArgs));

                        if (FAILED(ec)) {
                            MARSHAL_DBGOUT(MSHDBG_ERROR, printf(
                              "MshUserToKern: Unmarshal interface, ec = %x\n",
                              ec));
                            return ec;
                        }
                    }
                    else {
                        *((IInterface **)*puArgs) = NULL;
                    }
                }
                puData = (UInt32 *)((Byte *)puData + sizeof(InterfacePack));
                puArgs++;
                break;

            default:
                MARSHAL_DBGOUT(MSHDBG_ERROR, printf(
                    "MshProc: Invalid [in, out] type(%08x).\n",
                    pParams[n]));
                assert(0);
                return E_INVALID_ARGUMENT;
        }
    }

    return ec;
}
